package kd.bos.openapi.sdk.util;

import kd.bos.openapi.sdk.exception.APIException;
import kd.bos.openapi.sdk.response.result.ResultCode;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SignUtils {

    private static final String SIGNATURE_TIME_RANGE_MINUTES = "signature_time_range_minutes";

	private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    
    private static final String SHA256="HmacSHA256";
    
    public static String SIGNATUR_TIME_FORMAT="yyyy-MM-dd HH:mm:ss";
    public static String SIGNATUR_PARAMS="signature";


    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SignUtils.class);
    /**
     * 使用 HMAC-SHA1 签名方法对data进行签名
     * @param data 被签名的字符串
     * @param key 密钥
     * @return
    加密后的字符串
     */
    public static String genHmacSHA1(String data, String key){
    	try {
    		if(StringUtils.isEmpty(data)||StringUtils.isEmpty(key)){
        		return null;
        	}
            SecretKeySpec signKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA1_ALGORITHM);
            Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            mac.init(signKey);
            byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
            byte[] result = Base64.encodeBase64(rawHmac);

            if (null != result) {
                return new String(result,"UTF-8");
            } else {
                return null;
            }
    	}catch (Exception e) {
			// TODO: handle exception
    		logger.error(ExceptionUtils.getStackTrace(e));
    		throw APIException.of(ResultCode.BUSINESS_ERROR_CODE_EXCEPTION,e.getMessage());
		}
    }
    
    /**
     * 使用 HMAC-SHA1 签名方法对data进行签名
     * @param data 被签名的字符串
     * @param key 密钥
     * @return
    加密后的字符串
     */
    public static String genSHA256(String data, String key){
    	try {
    		if(StringUtils.isEmpty(data)||StringUtils.isEmpty(key)){
        		return null;
        	}
            SecretKeySpec signKey = new SecretKeySpec(key.getBytes("UTF-8"), SHA256);
            Mac mac = Mac.getInstance(SHA256);
            mac.init(signKey);
            byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
            byte[] result = Base64.encodeBase64(rawHmac);

            if (null != result) {
                return new String(result,"UTF-8");
            } else {
                return null;
            }
    	}catch (Exception e) {
			// TODO: handle exception
            logger.error(ExceptionUtils.getStackTrace(e));
            throw APIException.of(ResultCode.BUSINESS_ERROR_CODE_EXCEPTION,e.getMessage());
		}
    }
    
    public static String genSHA256(String data){
    	try {
    		if(StringUtils.isEmpty(data)){
        		return null;
        	}
          //  SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), SHA256);
            Mac mac = Mac.getInstance(SHA256);
           // mac.init(signKey);
            byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
            byte[] result = Base64.encodeBase64(rawHmac);

            if (null != result) {
                return new String(result,"UTF-8");
            } else {
                return null;
            }
    	}catch (Exception e) {
			// TODO: handle exception
            logger.error(ExceptionUtils.getStackTrace(e));
            throw APIException.of(ResultCode.BUSINESS_ERROR_CODE_EXCEPTION,e.getMessage());
		}
    	
    }
    
    /**
    * 利用java原生的摘要实现SHA256加密
    * @param str 加密后的报文
    * @return
    */
    public static String getSHA256StrJava1(String str){
	    MessageDigest messageDigest;
	    String encodeStr = "";
	    try {
		    messageDigest = MessageDigest.getInstance("SHA-256");
		    messageDigest.update(str.getBytes("UTF-8"));
		    encodeStr = byte2Hex(messageDigest.digest());
	    } catch (NoSuchAlgorithmException e) {
            logger.error(ExceptionUtils.getStackTrace(e));
	    } catch (UnsupportedEncodingException e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }
	    return encodeStr;
    }
    
    /**
     * 实现HMACSHA256加密算法
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String HMACSHA256StrByKey(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString();
    }
    /**
    * 将byte转为16进制
    * @param bytes
    * @return
    */
    private static String byte2Hex(byte[] bytes) {
        StringBuffer stringBuffer = new StringBuffer();
        String temp = null;
        for (int i = 0; i < bytes.length; i++) {
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length() == 1) {
                //1得到一位的进行补0操作
                stringBuffer.append("0");
            }
            stringBuffer.append(temp);
        }
        return stringBuffer.toString();
    }
    /**
     * 根据key，生成摘要
     * @param signKey
     * @param openId
     * @param sourceType
     * @param sourceIds
     * @return
     * @throws Exception
     */
    public static String genSign(String signKey, String openId, String sourceType, List<String> sourceIds) throws Exception {
        List<String> vals = new ArrayList<>();
        vals.add(openId);
        vals.add(sourceType);
      /*  if (sourceIds!=null&&sourceIds.size()>0){
            for (int i = 0; i < sourceIds.size(); i++) {
                String sourceId = sourceIds.get(i);
                vals.add(sourceId);
            }
        }*/
        vals.addAll(sourceIds);
        
        Collections.sort(vals, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        String sortedVal = join(vals, "&");

        return SignUtils.genHmacSHA1(sortedVal, signKey);
    }
    
    private static String join(List<String> list ,String sign){
    	StringBuilder returnValue=new StringBuilder();
    	if(list!=null){
    		for(int i=0;i<list.size();i++){
    			if(i!=0){
    				returnValue.append(sign);
    			}
    			returnValue.append(list.get(i));
    		}
    	}
    	return returnValue.toString();
    }
    /**
     * 获取设置的签名软件最大时间长度，默认10分钟
     * @return
     */
    // 
    public static int getSignRangeMinutes(String tenantId) {
        int returnValue = 10;//默认10分钟
        String signRangeMinuteString = System.getProperty(tenantId + "_" + SIGNATURE_TIME_RANGE_MINUTES);
        if (StringUtils.isEmpty(signRangeMinuteString)) {
            signRangeMinuteString = System.getProperty(SIGNATURE_TIME_RANGE_MINUTES);
            return returnValue;
        }

        try {
            returnValue = Integer.parseInt(signRangeMinuteString);
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }

        return returnValue;
    }

    public static void main(String[] args) throws Exception {
        String orgin = "s46你好";
        logger.info("orgin=" + orgin);
        String key = "";
        logger.info("key=" + key);
        try {
            logger.info("sign=" + SignUtils.genSHA256(orgin, key));
            logger.info("sign2=" + SignUtils.getSHA256StrJava1(orgin));
            logger.info("encrypt=" + HMACSHA256StrByKey(orgin, key));
        } catch (Exception e) {
            // TODO Auto-generated catch block
            logger.error(ExceptionUtils.getStackTrace(e));
        }
    }
}
